package br.academia.controller;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.faces.application.Application;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.beanutils.BeanUtils;

import br.academia.arq.dao.GenericDAO;
import br.academia.arq.erros.DAOException;
import br.academia.dominio.Usuario;
import br.academia.util.AcademiaUtil;
import br.academia.util.Constants;
import br.academia.util.OrderedSelectItem;

/**
 * Classe que implementa o padrão Layer Supertype
 *
 */
@SuppressWarnings("serial")
public class AbstractController implements Serializable{
	public static final String HOME_JSP = "/index.jsf";
	
	/** Operação realizada com sucesso. */
	protected static String mensagemSucesso = "Operação realizada com sucesso."; 
	
	/** Combo para meses */
	private static List<SelectItem> meses = new ArrayList<SelectItem>();
	/** Combo para anos */
	private static List<SelectItem> anos = new ArrayList<SelectItem>();
	/** Combo booleano */
	private static List<SelectItem> simNao = new ArrayList<SelectItem>();
	/** Combo para sexo */
	private static List<SelectItem> mascFem = new ArrayList<SelectItem>();
	
	static{
		/** Combo para meses */
		meses.add(new SelectItem("1", "Janeiro"));
		meses.add(new SelectItem("2", "Fevereiro"));
		meses.add(new SelectItem("3", "Março"));
		meses.add(new SelectItem("4", "Abril"));
		meses.add(new SelectItem("5", "Maio"));
		meses.add(new SelectItem("6", "Junho"));
		meses.add(new SelectItem("7", "Julho"));
		meses.add(new SelectItem("8", "Agosto"));
		meses.add(new SelectItem("9", "Setembro"));
		meses.add(new SelectItem("10", "Outubro"));
		meses.add(new SelectItem("11", "Novembro"));
		meses.add(new SelectItem("12", "Dezembro"));
		
		/** Combo para anos */
		for (int i = Constants.ANO_INCIO_PADRAO; i <= AcademiaUtil.getAnoAtual() + 4; i++) {
			anos.add(new SelectItem(String.valueOf(i), String.valueOf(i)));
		}
		
		/** Combo booleano */
		simNao.add(new SelectItem(String.valueOf(true), "Sim"));
		simNao.add(new SelectItem(String.valueOf(false), "Não"));
		
		/** Combo para sexo */
		mascFem.add(new SelectItem(Constants.FEMININO, "Feminino"));
		mascFem.add(new SelectItem(Constants.MASCULINO, "Masculino"));
		
	}
	
	public AbstractController(){
		
	}
	
	/**
	 * Método usado para se pegar um parâmetro da request
	 * @param Nome do parametro
	 * @return Parametro
	 */
	public String getParameter(String param) {
		return getCurrentRequest().getParameter(param);
	}
	
	/**
	 * Retorna o usuário logado
	 * @return
	 */
	public Usuario getUsuarioLogado() {
		return (Usuario) getCurrentRequest().getSession().getAttribute(
				Constants.USUARIO_LOGADO);
	}
	
	/**
	 * Retorna o usuário logado
	 * @return
	 */
	public void setUsuarioLogado(Usuario usuarioLogado) {
		getCurrentRequest().getSession().setAttribute(Constants.USUARIO_LOGADO, usuarioLogado);
	}
	
	/**
	 * Retorna o nome do mes passado como parametro
	 * @param mes
	 * @return Nome do mes em português 
	 */
	public String getMes(int mes) {
		return meses.get(mes).getLabel();
	}

	/**
	 * Método para o padrão de data
	 * @return Padrão de dd/MM/yyyy
	 */
	public String getPadraoData() {
		return "dd/MM/yyyy";
	}

	/**
	 * Método para o padão de números
	 * @return Padrão #,##0.00
	 */
	public String getPadraoNumero() {
		return "#,##0.00";
	}
	
	/**
	 * Combo de meses
	 * @return Combo de meses em português
	 */
	public List<SelectItem> getMeses() {
		return meses;
	}
	
	/**
	 * Combo para booleano
	 * @return Combo com sim, não
	 */
	public List<SelectItem> getSimNao() {
		return simNao;
	}

	/**
	 * Combo para Sexo
	 * @return Combo para escolha do sexo
	 */
	public List<SelectItem> getMascFem() {
		return mascFem;
	}
	
	/**
	 * Compo para anos
	 * @return Combo para a escolha de um determinado ano
	 * @throws Exception
	 */
	public List<SelectItem> getAnos() throws Exception {
		return anos;
	}

	/**
	 * Metodo utilitário
	 * @return Ano atual
	 */
	public int getAnoAtual() {
		return AcademiaUtil.getAnoAtual();
	}
	
	/**
	 * Metodo utilitário
	 * @return Mes atual como um int
	 */
	public int getMesAtual() {
		return AcademiaUtil.getMesAtual();
	}

	/**
	 * Metodo utilitário
	 * @return Nome do Mes atual em português
	 */
	public String getMesAtualString() {
		return getMes(getMesAtual());
	}

	/**
	 * Divide o ano em dois periodos diferentes
	 * @return 1 se no primeiro periodo, 2 se no segundo
	 */
	public int getPeriodoAtual() {
		return (getMesAtual() + 1) < 7 ? 1 : 2;
	}
	
	/**
	 * Metodo usando pelas jsf para iniciar um mbean
	 * Usando como em: <h:outputText value="#{bean.create}" />
	 * @return
	 * @throws DAOException 
	 */
	public String getCreate() throws DAOException {
		return "";
	}
	
	/**
	 * Informa o diretório base de redirecionamento
	 *
	 * @param dirBase
	 */
	public void setDirBase(String dirBase) {
		getCurrentSession().setAttribute("dirBase", dirBase);
	}
	
	/**
	 * Retorna um managed-bean existente no container do JavaServer Faces.
	 */
	@SuppressWarnings({ "unchecked", "deprecation" })
	public static <T> T getController(String mbeanName) {
		FacesContext fc = FacesContext.getCurrentInstance();
		return (T) fc.getApplication().getVariableResolver().resolveVariable(fc, mbeanName);
	}
	
	/**
	 * Transforma um mapa em uma colecao de SelectItem. A key de uma Entry
	 * será utilizado como value do SelectItem e o value de uma Entry 
	 * como label.
	 */
	public static Collection<SelectItem> toSelectItems(Map<?, ?> map) {
		Collection<SelectItem> itens = new ArrayList<SelectItem>();

		for (Iterator<?> it = map.entrySet().iterator(); it.hasNext();) {
			Entry<?, ?> entry = (Entry<?, ?>) it.next();
			itens.add(new SelectItem(entry.getKey().toString(), entry.getValue().toString()));
		}
		return itens;
	}
	
	 /**
	  * Transforma uma coleção de objetos em uma lista de SelectItems
	  * @param col Coleção a ser transformada em SelectItems
	  * @param value Atributo que será utilizado no value do option
	  * @param showText Valor que será exibido ao usu�rio
	  */
	public static List<SelectItem> toSelectItems(Collection<?> col, String value, String showText) {

		ArrayList<OrderedSelectItem> itensOrdenaveis = new ArrayList<OrderedSelectItem>();
		ArrayList<SelectItem> itens = new ArrayList<SelectItem>();

		try {
			if (col != null) {
				for (Iterator<?> it = col.iterator(); it.hasNext();) {
					Object obj = it.next();
					Object id = AcademiaUtil.evalProperty(obj, value);
					Object text = AcademiaUtil.evalProperty(obj, showText);
					if (text == null) {
						text = "";
					}
					OrderedSelectItem item = new OrderedSelectItem(id.toString(), text
							.toString());
					itensOrdenaveis.add(item);

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		Collections.sort(itensOrdenaveis);
		for ( OrderedSelectItem item : itensOrdenaveis ) {
			itens.add(item.toSelectItem());
		}

		return itens;

	}
	
	/**
	 * Retorna a inst�ncia do DAO
	 * @param daoClass Herda de GenericDAO
	 * @return A inst�ncia do DAO
	 * @throws DAOException
	 */
	public <T extends GenericDAO> T getDAO(Class<T> daoClass)throws DAOException {

		try {
			T dao = daoClass.newInstance();
			return dao;

		} catch (Exception e) {
			throw new DAOException("Erro ao obter DAO " + e.getMessage(), e);
		}

	}
	
	public GenericDAO getGenericDAO() throws DAOException{
		return getDAO(GenericDAO.class);
	}
	
	/**
	 * Transforma uma coleção de objetos em uma lista de SelectItems
	 * @param col Coleção a ser transformada em SelectItems
	 * @param showText Valor que será exibido ao usuário
	 * @return Lista de SelectItems correspondente
	 */
	public static List<SelectItem> toSelectItems(Collection<?> col, String showText) {
		List<SelectItem> items = new ArrayList<SelectItem>();
		
		try {
			if (col != null) {
				for (Object obj : col) {
					items.add(new SelectItem(obj, BeanUtils.getProperty(obj, showText)));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return items;
	}
	

	/**
	 * Redireciona o usu�rio para uma url concatenando
	 * o contexto da aplica��o.
	 */
	public String redirect(String url) {
		String context = getContextPath();
		if (!url.startsWith(context))
			url = context + url;
		return redirectSemContexto(url);
	}
	
	/**
	 * Redireciona o usuário para uma url sem concatenar
	 * o contexto.
	 */
	public String redirectSemContexto(String url) {
		
		try {
			getCurrentResponse().sendRedirect(url);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		FacesContext.getCurrentInstance().responseComplete();
		return null;
	}
	
	/**
	 * Realiza um forward para a jsp passada como parâmetro.
	 * @param url
	 * @return null
	 */
	public String forward(String url) {
		FacesContext context = FacesContext.getCurrentInstance();
		Application app = context.getApplication();
		UIViewRoot view = app.getViewHandler().createView(context, url);
		context.setViewRoot(view);
		context.renderResponse();
		
		postarMensagens();
		
		// Retorna null para evitar o return null no action do Managed Bean
		return null;
	}
	
	public String entrarHome(){
		return forward(HOME_JSP);
	}
	
	public String reload(){
		postarMensagens();
		return null;
	}
	
	/**
	 * Possibilita o acesso ao HttpServletRequest.
	 */
	public HttpServletRequest getCurrentRequest() {
		return (HttpServletRequest) getExternalContext().getRequest();
	}
	
	/**
	 * Retorna o contexto da aplicação WEB
	 */
	public String getContextPath() {
		return getCurrentRequest().getContextPath();
	}
	
	/**
	 * Possibilita o acesso ao HttpServletResponse.
	 */
	public HttpServletResponse getCurrentResponse() {
		return (HttpServletResponse) getExternalContext().getResponse();
	}
	
	/**
	 * Possibilita o acesso ao HttpSession.
	 */
	public HttpSession getCurrentSession() {
		return getCurrentRequest().getSession(true);
	}
	
	/**
	 * Acessa o external context do JavaServer Faces
	 */
	private ExternalContext getExternalContext() {
		return FacesContext.getCurrentInstance().getExternalContext();
	}
	
	public void addMensagem(String msg){
		InformacoesController info = getController("informacoes");
		info.addMensagem(msg);
	}
	
	public boolean hasMensagens(){
		InformacoesController info = getController("informacoes");
		return !info.getMensagens().isEmpty();
	}
	
	public void postarMensagens(){
		InformacoesController infos = getController("informacoes");
		
		infos.setMensagensView(infos.getMensagens());
		infos.reset();
	}
	
	public <T extends GenericDAO> void close(T dao){
		if(dao != null){
			dao.close();
		}
	}
}
